﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using GpgApi;
using Microsoft.Office.Core;
using Microsoft.Office.Interop.Outlook;
using Microsoft.Office.Tools.Ribbon;
using WgpgCore;
using WgpgGUI;

namespace WgpgOutlookPlugin
{
    // http://ahmedmuneeb1.wordpress.com/2009/04/13/reading-emails-email-headers-from-outlook-mail-using-c-part-i/

    public partial class WgpgAddIn
    {
        private void ThisAddIn_Startup(Object sender, EventArgs e)
        {
            GpgInterface.ExePath = Config.Instance.GpgPath;
            Application.ItemSend += EncryptEmailBeforeSend;
        }

        private void ThisAddIn_Shutdown(Object sender, EventArgs e)
        {
        }

        private void EncryptEmailBeforeSend(Object Item, ref Boolean Cancel)
        {
            if (!Globals.Ribbons.Ribbon1.encrypt.Checked)
                return;

            MailItem mailItem = Item as MailItem;
            if (mailItem == null)
                return;

            // ------------------------------------------------------------------------------------
            // First, we get the content of the email

            String content = String.Empty;
            OlBodyFormat format = mailItem.BodyFormat;
            if (format == OlBodyFormat.olFormatHTML)
                content = mailItem.HTMLBody;
            else if (format == OlBodyFormat.olFormatRichText)
                content = mailItem.RTFBody;
            else
                content = mailItem.Body;

            // ------------------------------------------------------------------------------------

            String filename = Utils.GetTempFileName();
            String encryptedFilename = filename + ".encrypted";

            // ------------------------------------------------------------------------------------
            // We save this content to disk (unfortunately, it is not yet possible to use a memory stream)

            using (StreamWriter wtr = new StreamWriter(filename))
            {
                wtr.Write(content);
            }

            // ------------------------------------------------------------------------------------
            // The content is saved, now starts encryption

            List<KeyId> publicKeyIds = KeyManager.GetKeyIdsByEmail(OutlookUtils.GetRecipientsFromMailItem(mailItem));

            EncryptionDialog encryptDialog = new EncryptionDialog
            {
                ASCIIArmored = true,
                UseCipherAlgorithm = false,
                CipherAlgorithm = CipherAlgorithm.Aes256,
                EncryptedFileName = encryptedFilename,
                HideUserId = false,
                OriginalFileName = filename,
                SignFile = false,
                UsePublicKey = true,
            };
            //encryptDialog.PublicRecipients.AddRange(keyIds);

            if (encryptDialog.ShowDialog() == false)
            {
                Cancel = true;
                return;
            }

            if (!encryptDialog.UseCipherAlgorithm)
                encryptDialog.CipherAlgorithm = CipherAlgorithm.None;

            if (!encryptDialog.UsePublicKey)
                encryptDialog.PublicRecipients.Clear();

            GpgEncrypt encrypt = new GpgEncrypt(filename, encryptedFilename, encryptDialog.ASCIIArmored, encryptDialog.HideUserId, encryptDialog.SignatureKeyId, encryptDialog.PublicRecipients, encryptDialog.CipherAlgorithm);
            encrypt.AskPassphrase = GUIUtils.AskPassphrase();

            GpgInterfaceResult result = encrypt.Execute();

            // ------------------------------------------------------------------------------------
            // There is an error, do not send the email.

            if (result.Status == GpgInterfaceStatus.Error)
            {
                if (result.Message == GpgInterfaceMessage.SignatureKeyExpired)
                    MessageBox.Show("The signature key you selected is expired");
                else if (result.Message == GpgInterfaceMessage.InvalidRecipient)
                    MessageBox.Show(String.Format("{0} is an invalid recipient", result.Data.ToString()));
                else
                    MessageBox.Show("Undefined error, please retry");

                Cancel = true;
                return;
            }

            // ------------------------------------------------------------------------------------
            // Read the encrypted content from disk

            using (StreamReader rdr = new StreamReader(encryptedFilename))
            {
                content = rdr.ReadToEnd();
            }

            // ------------------------------------------------------------------------------------
            // Replace the content of the email with the encrypted content

            format = OlBodyFormat.olFormatPlain;
            mailItem.Body = content;

            // ------------------------------------------------------------------------------------
            // Delete temp files

            File.Delete(filename);
            File.Delete(encryptedFilename);
        }

        protected override IRibbonExtensibility CreateRibbonExtensibilityObject()
        {
            return Globals.Factory.GetRibbonFactory().CreateRibbonManager(new IRibbonExtension[] { new WgpgRibbon() });
        }

        public void Decrypt(Object Context, Boolean userAction = false)
        {
            MailItem mailItem = GetMailItem(Context);
            if (mailItem == null)
                return;

            String content = mailItem.Body;
            if (!ArmoredMessage.HasPgpMessage(content))
            {
                MessageBox.Show("This email is not encrypted");
                return;
            }

            String tmp = Utils.GetTempFileName();
            List<String> messages = ArmoredMessage.ExtractPgpMessages(content);
            Encoding enc = Encoding.GetEncoding(mailItem.InternetCodepage);
            using (StreamWriter wtr = new StreamWriter(tmp, false, enc))
            {
                wtr.Write(messages[0]);
                wtr.Flush();
            }

            String decrypted_tmp = Utils.GetTempFileName();
            String decrypted_content = null;

            System.Windows.Forms.Application.DoEvents();
            GpgInterface.SynchronizationContext = SynchronizationContext.Current;

            GpgDecrypt decrypt = new GpgDecrypt(tmp, decrypted_tmp);
            decrypt.AskPassphrase = GUIUtils.AskPassphrase();
            decrypt.ExecuteAsync((result) =>
            {
                if (result.Status == GpgInterfaceStatus.Error)
                {
                    if (result.Message == GpgInterfaceMessage.BadPassphrase)
                        MessageBox.Show("Bad passphrase");
                    return;
                }

                if (result.Status == GpgInterfaceStatus.Success)
                {
                    using (StreamReader rdr = new StreamReader(decrypted_tmp, Encoding.GetEncoding(mailItem.InternetCodepage)))
                    {
                        decrypted_content = rdr.ReadToEnd();
                    }

                    if (decrypted_content != null)
                    {
                        if (decrypted_content.StartsWith("<html"))
                        {
                            mailItem.BodyFormat = OlBodyFormat.olFormatHTML;
                            mailItem.HTMLBody = decrypted_content;
                        }
                        else
                        {
                            mailItem.BodyFormat = OlBodyFormat.olFormatPlain;
                            mailItem.Body = decrypted_content;
                        }
                    }
                }
            });
        }

        private MailItem GetMailItem(Object Context)
        {
            Inspector inspector = Context as Inspector;
            if (inspector == null)
                return null;

            return inspector.CurrentItem as MailItem;
        }

        #region VSTO generated code

        private void InternalStartup()
        {
            Startup += ThisAddIn_Startup;
            Shutdown += ThisAddIn_Shutdown;
        }

        #endregion
    }
}
